/* the 3D Matrix's structure
* [	[xx,yx,zx,wx]
* 	[xy,yy,zy,wy]
* 	[xz,yz,zz,wz]
* 	[xw,yw,zw,s ] ]
* */

dynamic class D3.Matrix{
	public var matrixArray:Array;
	public static var say:Function;
	
	public function Matrix(obj:Object){
		this.matrixArray=new Array();
		if(obj==undefined){
			this.matrixArray=[	[1,0,0,0],
							[0,1,0,0],
							[0,0,1,0],
							[0,0,0,1]	];
		}
		if(obj[0][0]!=undefined){
			for(var i=0;i<obj.length;i++){
				this.matrixArray[i]=new Array();
				for(var ii=0;ii<obj[i].length;ii++){
					this.matrixArray[i][ii]=obj[i][ii];
				}
			}
		}
	}
	
	///Matrix 
	public function X(m2:Matrix):Matrix{
		if(m2==undefined)
		{
			return this;
		}
		if(this.matrixArray[0].length!= m2.matrixArray.length)
		{
			Say("the argument's structrue isn't match!");
			return null;
		}
		
		return Matrix.MatrixX(this,m2);	
	}
	public static function MatrixX(m1:Matrix,m2:Matrix):Matrix{
		if(m1==undefined )
		{
			return m1;
		}else if(m2==undefined){
			return m2;
		}
		
		if(m1.matrixArray[0].length!= m2.matrixArray.length)
		{
			say("the argument's structrue isn't match!");
			return null;
		}
		var rma:Array=new Array();
		for(var i=0;i<m1.matrixArray.length;i++){
			rma[i]=new Array();
			for(var ii=0;ii<m2.matrixArray[0].length;ii++){
				var sum:Number=0;
				for(var iii=0;iii<m1.matrixArray[0].length;iii++){
					sum+=m1.matrixArray[i][iii]*m2.matrixArray[iii][ii];
				}
				rma[i].push(sum);
			}
		}
		return new Matrix(rma);
	}
	///Matrix return the X-Axes Rotation transform Matrix
	public static function getXRotation(O:Number):Matrix{
		return new Matrix([[1,0,0,0],[0,Math.cos(O),Math.sin(O),0],[0,-Math.sin(O),Math.cos(O),0],[0,0,0,1]]);
	}
	///Matrix return the Y-Axes Rotation transform Matrix
	public static function getYRotation(O:Number):Matrix{
		return new Matrix([[Math.cos(O),0,-Math.sin(O),0],[0,1,0,0],[Math.sin(O),0,Math.cos(O),0],[0,0,0,1]]);
	}
	///Matrix return the Z-Axes Rotation transform Matrix
	public static function getZRotation(O:Number):Matrix{
		return new Matrix([[Math.cos(O),Math.sin(O),0,0],[-Math.sin(O),Math.cos(O),0,0],[0,0,1,0],[0,0,0,1]]);
	}
	///Matrix return the 3-Axes Rotation transform Matrix
	public static function getFreeAxisRotation(x:Number,y:Number,z:Number,O:Number):Matrix{
		if(x==0 && y==0){
			return Matrix.getZRotation(O);
		}else if(x==0 && z==0){
			return Matrix.getYRotation(O);
		}else if(y==0 && z==0){
			return Matrix.getXRotation(O);
		}
		var a1:Number=Math.PI/2;
		var a2:Number=0;
		var y2:Number=Math.pow(y*y+z*z,0.5);
		if(z!=0){
			a1=Math.atan2(y,z);
			a2=-Math.atan2(x,y2);
		}else{
			a2=-Math.atan2(x,y2);
		}
		
		
		//trace(a1+" ||| "+a2);
		var m:Matrix=Matrix.getXRotation(a1);
		m=m.X(Matrix.getYRotation(a2));
		m=m.X(Matrix.getZRotation(O));
		m=m.X(Matrix.getYRotation(-a2));
		m=m.X(Matrix.getXRotation(-a1));
		return m;
	}
	///Matrix return movement transform
	public static function getMovement(x:Number,y:Number,z:Number):Matrix{
		return new Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [x, y, z, 1]]);
	}
	///Matrix return scalment transform
	public static function getScal(x:Number,y:Number,z:Number):Matrix{
		return new Matrix([[x, 0, 0, 0], [0, y, 0, 0], [0, 0, z, 0], [0,0,0, 1]]);
	}
	
	
	public function toString():String{
		var str:String="\n";
		for(var i=0;i<this.matrixArray.length;i++){
			str+=this.matrixArray[i].join(",")+"\n";
		}
		return str;
	}
	
	private function Say(o:Object){
		var msg:String="Matrix err: "+o.toString();
		if(Matrix.say!=undefined){
			Matrix.say(msg);
		}else{
			trace(msg);
		}
	}
}